using System.Net.Http.Headers;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Yarp.ReverseProxy.Transforms;

namespace Web.Bff;

public static class DependencyInjection
{
    /// <summary>
    /// 添加BFF认证和安全服务
    /// </summary>
    public static IServiceCollection AddSecurity(this IServiceCollection services, 
        IConfiguration configuration)
    {
        // 添加认证服务
        services.AddAuthenticationServices(configuration);
        // 添加授权服务
        services.AddAuthorization();
        // 添加防伪令牌服务
        services.AddAntiforgeryServices();
        // 添加HSTS服务
        services.AddHsts(options =>
        {
            options.Preload = true;
            options.IncludeSubDomains = true;
            options.MaxAge = TimeSpan.FromDays(60);
        });
        services.AddHttpContextAccessor();
        return services;
    }

    /// <summary>
    /// 添加服务发现和基础设施
    /// </summary>
    public static IServiceCollection AddServiceDiscovery(this IServiceCollection services, 
        IConfiguration configuration)
    {
        // 添加服务发现
        services.AddServiceDiscovery(options =>
        {
            options.AllowAllSchemes = true;
            options.RefreshPeriod = TimeSpan.FromSeconds(5);
        });
        return services;
    }

    /// <summary>
    /// 添加Yarp配置
    /// </summary>
    public static IServiceCollection ApplyYarp(this IServiceCollection services, 
        IConfiguration configuration)
    {
        services.AddReverseProxy()
            .AddTransforms(ctx =>
            {
                // 路径转换
                ctx.AddPathRemovePrefix("/bff").AddPathPrefix("/api/v1/");
                // 自动添加Bearer token
                ctx.AddRequestTransform(async rct =>
                {
                    var token = await rct.HttpContext.GetTokenAsync("access_token");
                    if (!string.IsNullOrEmpty(token))
                    {
                        // 添加Bearer token到请求头
                        rct.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
                    }
                });
            })
            .LoadFromConfig(configuration.GetSection("ReverseProxy"))
            .AddServiceDiscoveryDestinationResolver();
        return services;
    }
    
    private static IServiceCollection AddAuthenticationServices(this IServiceCollection services, 
        IConfiguration configuration)
    {
        services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(o =>
            {
                //登录到Cookies方案，之后可以从cookies方案获取授权的相关信息
                o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                //发行人地址 Issuer/Authority
                o.Authority = configuration["OpenIDConnect:Authority"];
                o.ClientId = configuration["OpenIDConnect:ClientId"];
                o.ClientSecret = configuration["OpenIDConnect:ClientSecret"];
                //当强制启用Https得需要配置证书
                o.RequireHttpsMetadata = true;
                //采用授权码标准流程
                o.ResponseType = OpenIdConnectResponseType.Code;
                o.UsePkce = true;
                o.SaveTokens = true;
                o.GetClaimsFromUserInfoEndpoint = true;
                //所需访问的Scopes,可以在授权服务修改配置
                o.Scope.Add("bff_acceess_scope");
                o.BackchannelHttpHandler = new HttpClientHandler()
                {
                    //ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator,
                    ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) =>
                    {
                        // local dev, just approve all certs
                        return true;
                        //return errors == SslPolicyErrors.None;
                    }
                };
            });
        return services;
    }

    private static IServiceCollection AddAntiforgeryServices(this IServiceCollection services)
    {
        services.AddAntiforgery(options =>
        {
            options.HeaderName = "X-CSRF-TOKEN";
            options.Cookie.Name = "__Host-X-CSRF-TOKEN";
            options.Cookie.SameSite = SameSiteMode.Strict;
            options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        });
        return services;
    }
}
